//`include "timescale.v"
//`include "SD_defines.v"
`define tTLH 10 //Clock rise time
`define tHL 10 //Clock fall time
`define tISU 6 //Input setup time
`define tIH 0 //Input hold time
`define tODL 14 //Output delay
`define DLY_TO_OUTP 55

`define BLOCKSIZE 512
`define MEMSIZE 10240 // 4 block
`define BLOCK_BUFFER_SIZE 1
`define TIME_BUSY 64
`define BIT_CRC_CYCLE 16
`define CRC_OFF 16

`define PRG 7
`define RCV 6
`define DATAS 5
`define TRAN 4

module sdModel(
              sdClk,
              cmd,
              dat
              );

input sdClk;
inout cmd;
inout [7:0] dat;

wire  cmd;
wire  [7:0] dat;

reg stop;
reg oeCmd;
reg oeDat;
reg cmdOut;
reg [7:0] datOut;
reg [10:0] transf_cnt;

reg [7:0] block_num;
reg       set_block;

reg [2:0] write_crc_state_count;

//-----multi_block single 2010-08-05-------//
reg [31:0] Adrr_RD_REG;
reg [31:0] Adrr_WR_REG;
reg [15:0] Block_wr_cnt;
reg [15:0] Block_rd_cnt;
reg        multi_rd_en;
reg        multi_wr_en;
reg        Block_rd_en;
reg        Block_wr_en;
//-----------------------------------------//
wire [15:0] BLOCK_CNT_REG;  //2010-08-11

assign  BLOCK_CNT_REG = 8 ; //2010-08-11

reg [5:0] lastCMD;
reg cardIdentificationState;
reg CardTransferActive;
reg [3:0] BusWidth;

wire  cmd_up;
wire  dat_up;

assign #6 cmd = oeCmd ? cmdOut : 1'bz;
assign #6 dat = oeDat ? datOut : 8'bz;

reg InbuffStatus;
reg [31:0] BlockAddr;
reg [7:0] Inbuff [0:511];
reg [7:0] FLASHmem [0:`MEMSIZE];


reg [46:0]inCmd;
reg [5:0]cmdRead;
reg [7:0] cmdWrite;
reg crcIn;
reg crcEn;
reg crcRst;
reg [31:0] CardStatus;
reg [15:0] RCA;
reg [31:0] OCR;
reg [120:0] CID;
reg Busy; //0 when busy
wire [6:0] crcOut;
reg [4:0] crc_c;

reg [3:0] CurrentState;
reg [3:0] DataCurrentState;
`define RCASTART 16'hAE
`define OCRSTART 32'hff8000
`define STATUSSTART 32'h0
//`define CIDSTART 128'h012345671234567823456789345678 //Just some random data not really usefull anyway

`define CIDSTART 128'h3f89_0303_4e43_6172_6410_4ed9_7214_017c //Just some random data not really usefull anyway

`define outDelay 4
reg [2:0] outDelayCnt;
reg [9:0] flash_write_cnt;
reg [8:0] flash_blockwrite_cnt;

parameter SIZE = 10;
parameter CONTENT_SIZE = 40;
parameter
    IDLE   =  10'b0000_0000_01,
    READ_CMD   =  10'b0000_0000_10,
    ANALYZE_CMD	    =  10'b0000_0001_00,
    SEND_CMD	    =  10'b0000_0010_00;
reg [SIZE-1:0] state;
reg [SIZE-1:0] next_state;

parameter
    DATA_IDLE   =10'b0000_0000_01,
    READ_WAITS  =10'b0000_0000_10,
    READ_DATA  = 10'b0000_0001_00,
    WRITE_FLASH =10'b0000_0010_00,
    WRITE_DATA  =10'b0000_0100_00;
parameter okcrctoken = 4'b0101;
parameter invalidcrctoken = 4'b1111;
reg [SIZE-1:0] dataState;
reg [SIZE-1:0] next_datastate;

reg ValidCmd;
reg inValidCmd;

reg [7:0] response_S;
reg [135:0] response_CMD;
integer responseType;

reg [9:0] block_cnt;
reg wptr;
reg [2:0] wptr_1;
reg crc_ok;
reg [7:0] last_din;

reg crcDat_rst;
reg crcDat_en;
reg [7:0] crcDat_in;
wire [15:0] crcDat_out [7:0];


genvar i;
generate
for(i=0; i<8; i=i+1) begin:CRC_16_gen
  SD_CRC_16_ori CRC_16_i (crcDat_in[i],crcDat_en, sdClk, crcDat_rst, crcDat_out[i]);
end
endgenerate
SD_CRC_7_ori CRC_7(
crcIn,
crcEn,
sdClk,
crcRst,
crcOut);


reg appendCrc;
reg [5:0] startUppCnt = 0;

reg q_start_bit;
//Card initinCMd
//initial $readmemh("../bin/FLASH.txt",FLASHmem);
/*
integer k;
initial begin
	$display("Contents of Mem after reading data file:");
	for (k=0; k<10; k=k+1) $display("%d:%h",k,FLASHmem[k]);
end
*/
reg qCmd;
reg [2:0] crcCnt;

reg add_wrong_cmd_crc;
reg add_wrong_cmd_indx;
reg add_wrong_data_crc;

initial begin
  add_wrong_data_crc<=0;
  add_wrong_cmd_indx<=0;
  add_wrong_cmd_crc<=0;
  cardIdentificationState<=1;
  state<=IDLE;
  dataState<=DATA_IDLE;
  Busy<=1;
  oeCmd<=0;
  crcCnt<=0;
  CardTransferActive<=0;
  qCmd<=1;
  oeDat<=0;
  cmdOut<=0;
  cmdWrite<=0;
  InbuffStatus<=0;
  datOut<=0;
  inCmd<=0;
  BusWidth<=1;
  responseType=0;
  crcIn<=0;
  response_S<=0;
  crcEn<=0;
  crcRst<=0;
  cmdRead<=0;
  ValidCmd<=0;
  inValidCmd=0;
  appendCrc<=0;
  RCA<= `RCASTART;
  OCR<= `OCRSTART;
  CardStatus <= `STATUSSTART;
  CID<=`CIDSTART;
  response_CMD<=0;
  outDelayCnt<=0;
  crcDat_rst<=1;
  crcDat_en<=0;
  crcDat_in<=0;
  transf_cnt<=0;
  BlockAddr<=0;
  block_cnt <=0;
  wptr<=0;
  wptr_1 <= 0;
  transf_cnt<=0;
  crcDat_rst<=1;
  crcDat_en<=0;
  crcDat_in<=0;
  flash_write_cnt<=0;
  flash_blockwrite_cnt<=0;
end

//CARD logic

always @ (state or cmd or cmdRead or ValidCmd or inValidCmd or cmdWrite or outDelayCnt)
begin : FSM_COMBO
 next_state  = 0;
case(state)
IDLE: begin
   if (!cmd)
     next_state = READ_CMD;
  else
     next_state = IDLE;
end
READ_CMD: begin
  if (cmdRead>= 47)
     next_state = ANALYZE_CMD;
  else
     next_state =  READ_CMD;
 end
 ANALYZE_CMD: begin
  if ((ValidCmd  )   && (outDelayCnt >= `outDelay ))
     next_state = SEND_CMD;
  else if (inValidCmd)
     next_state =  IDLE;
 else
    next_state =  ANALYZE_CMD;
 end
 SEND_CMD: begin
    if (cmdWrite>= response_S)
     next_state = IDLE;
  else
     next_state =  SEND_CMD;

 end


 endcase
end


always @ (dataState or CardStatus or crc_c or flash_write_cnt or dat[0] )
begin : FSM_COMBODAT
 next_datastate  = 0;
case(dataState)
 DATA_IDLE: begin
   if (CardStatus[12:9]==`RCV )
     next_datastate = READ_WAITS;
   else if (CardStatus[12:9]==`DATAS )
     next_datastate = WRITE_DATA;
   else
     next_datastate = DATA_IDLE;
 end

 READ_WAITS: begin
   if ( dat[0] == 1'b0 )
     next_datastate =  READ_DATA;
   else if (stop == 1'b1)
     next_datastate = DATA_IDLE;
   else
     next_datastate =  READ_WAITS;
 end

 READ_DATA : begin
  if (crc_c==0  )
     next_datastate =  WRITE_FLASH;
  else if (stop == 1'b1)
     next_datastate = DATA_IDLE;
  else
     next_datastate =  READ_DATA;
 end

 WRITE_FLASH : begin
  if (flash_write_cnt>265 )
     next_datastate =  DATA_IDLE;
//   else if (stop == 1'b1)
//     next_datastate = DATA_IDLE;
  else
     next_datastate =  WRITE_FLASH;
end
 WRITE_DATA : begin
    if (transf_cnt >= `BLOCKSIZE + 11'd19)
       next_datastate= DATA_IDLE;
   else if (stop == 1'b1)
     next_datastate = DATA_IDLE;
    else
       next_datastate=WRITE_DATA;
  end


 endcase
end


always @ (posedge sdClk  )
 begin
    q_start_bit <= dat[0];
 end

always @ (posedge sdClk  )
begin : FSM_SEQ
    state <= next_state;
end

always @ (posedge sdClk  )
begin : FSM_SEQDAT
    dataState <= next_datastate;
end



always @ (posedge sdClk) begin
if (CardTransferActive) begin
 if (InbuffStatus==0) //empty
   CardStatus[8]<=1;
  else
   CardStatus[8]<=0;
  end
else
  CardStatus[8]<=0;

 startUppCnt<=startUppCnt+1;
 OCR[31]<=Busy;
 if (startUppCnt == `TIME_BUSY)
   Busy <=1;
end


always @ (posedge sdClk) begin
   qCmd<=cmd;
end

reg boot;
//read data and cmd on rising edge
always @ (posedge sdClk) begin
 case(state)
   IDLE: begin

      crcIn<=0;
      crcEn<=0;
      crcRst<=1;
      oeCmd<=0;
      stop <= 1'b0;

      cmdRead<=0;
      appendCrc<=0;
      ValidCmd<=0;
      inValidCmd=0;
      cmdWrite<=0;
      crcCnt<=0;
      response_CMD<=0;
      response_S<=0;
      outDelayCnt<=0;
      responseType=0;
    end
   READ_CMD: begin //read cmd
      crcEn<=1;
      crcRst<=0;
      crcIn <= #`tIH qCmd;
      inCmd[47-cmdRead]  <= #`tIH qCmd;
      cmdRead <= #1 cmdRead+1;
      if (cmdRead >= 40)
         crcEn<=0;

      if (cmdRead == 46) begin
          oeCmd<=1;
     cmdOut<=1;
      end
   end

   ANALYZE_CMD: begin//check for valid cmd
   //Wrong CRC go idle
    if (inCmd[46] == 0) //start
      inValidCmd=1;
    else if (inCmd[7:1] != crcOut) begin
      inValidCmd=1;
      $fdisplay(sdModel_file_desc, "**sd_Model Commando CRC Error") ;
      $display(sdModel_file_desc, "**sd_Model Commando CRC Error") ;
    end
    else if  (inCmd[0] != 1)  begin//stop
      inValidCmd=1;
      $fdisplay(sdModel_file_desc, "**sd_Model Commando No Stop Bit Error") ;
      $display(sdModel_file_desc, "**sd_Model Commando No Stop Bit Error") ;
    end
    else begin
      if(outDelayCnt ==0)
        CardStatus[3]<=0;
      $display("recv cmd:%d",inCmd[45:40]);
      case(inCmd[45:40])
        0 : response_S <= 0;
        1 : response_S <= 48;
        2 : response_S <= 136;
        3 : response_S <= 48;
        6 : response_S <= 48;
        7 : response_S <= 48;
        8 : response_S <= 48;
        //12 : response_S <= 48;
        14 : response_S <= 0;
        16 : response_S <= 48;
        17 : response_S <= 48;
        23 : response_S <= 48;
        24 : response_S <= 48;
        18 : response_S <= 48;
        25 : response_S <= 48;
        12 : response_S <= 48;
        33 : response_S <= 48;
        55 : response_S <= 48;
        41 : response_S <= 48;
    endcase
         case(inCmd[45:40])
        0 : begin
            if (inCmd[39:8] == 32'hf0f0f0f0)
             begin
             response_S <= 48;
             CardStatus[12:9] <=`DATAS;
             multi_rd_en <= 1;
             set_block <= 1;
             block_num <= 8'h2;
             boot <= 1;
     //        BlockAddr <= 32'h00000000;
             end
            else
             begin
             response_CMD <= 0;
             cardIdentificationState<=1;
             ResetCard;
             end
            end
        1 : begin
            response_S<=48;
            response_CMD[127:96] <= OCR;
            end
        2 : begin
         if (lastCMD != 41 && outDelayCnt==0) begin
               $fdisplay(sdModel_file_desc, "**Error in sequnce, ACMD 41 should precede 2 in Startup state") ;
               //$display(sdModel_file_desc, "**Error in sequnce, ACMD 41 should precede 2 in Startup state") ;
               CardStatus[3]<=1;
            end
        response_CMD[127:8] <= CID;
        response_CMD[135:128] <= 'h3f;
        responseType = 3;
        appendCrc<=0;
        CardStatus[12:9] <=2;
        end
        3 :  begin
           if (lastCMD != 2 && outDelayCnt==0 ) begin
               $fdisplay(sdModel_file_desc, "**Error in sequnce, CMD 2 should precede 3 in Startup state") ;
               //$display(sdModel_file_desc, "**Error in sequnce, CMD 2 should precede 3 in Startup state") ;
               CardStatus[3]<=1;
            end
        response_CMD[127:112] <= RCA[15:0] ;
        response_CMD[111:96] <= CardStatus[15:0] ;
        appendCrc<=1;
        CardStatus[12:9] <=3;
        cardIdentificationState<=0;
       end
        6 : begin
//           if (lastCMD == 55 && outDelayCnt==0) begin
              if (inCmd[9:8] == 2'b11) begin
               BusWidth <=8;
                    $display(sdModel_file_desc, "**BUS WIDTH 8 ") ;
                  end
              else if (inCmd[9:8] == 2'b10) begin
               BusWidth <=4;
                    $display(sdModel_file_desc, "**BUS WIDTH 4 ") ;
               end
              else if (inCmd[9:8] == 2'b00)
               BusWidth <=1;

              response_S<=48;
              response_CMD[127:96] <= CardStatus;
           end
/*
           else if (outDelayCnt==0)begin
             response_CMD <= 0;
             response_S<=0;
             $fdisplay(sdModel_file_desc, "**Error Invalid CMD, %h",inCmd[45:40]) ;
           //  $display(sdModel_file_desc, "**Error Invalid CMD, %h",inCmd[45:40]) ;
            end
        end
*/
        7: begin
         if (outDelayCnt==0) begin
          if (inCmd[39:24]== RCA[15:0]) begin
              CardTransferActive <= 1;
              response_CMD[127:96] <= CardStatus ;
              CardStatus[12:9] <=`TRAN;
          end
          else begin
               CardTransferActive <= 0;
               response_CMD[127:96] <= CardStatus ;
               CardStatus[12:9] <=3;
          end
         end
        end
        8 : begin
           response_CMD[127:96] <= 'h1aa; //V1.0 card
        end
        12: begin
                response_CMD[127:96] <= CardStatus ;
                CardStatus[12:9] <= `TRAN ;
                stop <= 1'b1;
            end
        16 : begin
          response_CMD[127:96] <= CardStatus ;

        end
        17 :  begin
        	multi_rd_en<=0;
          if (outDelayCnt==0) begin
            if (CardStatus[12:9] == `TRAN) begin //If card is in transferstate
                CardStatus[12:9] <=`DATAS;//Put card in data state
                response_CMD[127:96] <= CardStatus ;
                BlockAddr = inCmd[39:8];
                if (BlockAddr%512 !=0)
                  $display("**Block Misalign Error");
          end
           else begin
             response_S <= 0;
             response_CMD[127:96] <= 0;
           end
         end

       end

        23 : begin
        	set_block <= 1;
        	block_num <= inCmd[23:8];
          response_CMD[127:96] <= CardStatus ;

        end

        24 : begin
          if (outDelayCnt==0) begin
            if (CardStatus[12:9] == `TRAN) begin //If card is in transferstate
              if (CardStatus[8]) begin //If Free write buffer
                CardStatus[12:9] <=`RCV;//Put card in Rcv state
                response_CMD[127:96] <= CardStatus ;
                BlockAddr = inCmd[39:8];
                if (BlockAddr%512 !=0)
                  $display("**Block Misalign Error");
              end
              else begin
                response_CMD[127:96] <= CardStatus;
                 $fdisplay(sdModel_file_desc, "**Error Try to blockwrite when No Free Writebuffer") ;
                 $display("**Error Try to blockwrite when No Free Writebuffer") ;
             end
           end
           else begin
             response_S <= 0;
             response_CMD[127:96] <= 0;
           end
         end

       end
 //-------------------------------2010-08-05-------------------------------//
        18 : begin
         if (outDelayCnt==0) begin
           if (CardStatus[12:9] == `TRAN) begin //If card is in transferstate
               CardStatus[12:9] <=`DATAS;//Put card in data state
               response_CMD[127:96] <= CardStatus ;
                   Adrr_RD_REG = inCmd[39:8];
                 multi_rd_en<=1;
              // if (BlockAddr%512 !=0)
              //  $display("**Block Misalign Error");
         end
          else begin
            response_S <= 0;
            response_CMD[127:96] <= 0;
          end
        end

      end

        25 : begin
          if (outDelayCnt==0) begin
            if (CardStatus[12:9] == `TRAN) begin //If card is in transferstate
              if (CardStatus[8]) begin //If Free write buffer
                CardStatus[12:9] <=`RCV;//Put card in Rcv state
                response_CMD[127:96] <= CardStatus ;
                    BlockAddr = inCmd[39:8];
                      Adrr_WR_REG = inCmd[39:8];
                  multi_wr_en<=1;
               // if (BlockAddr%512 !=0)
               //   $display("**Block Misalign Error");
              end
              else begin
                response_CMD[127:96] <= CardStatus;
                 $fdisplay(sdModel_file_desc, "**Error Try to blockwrite when No Free Writebuffer") ;
                 $display("**Error Try to blockwrite when No Free Writebuffer") ;
             end
           end
           else begin
             response_S <= 0;
             response_CMD[127:96] <= 0;
           end
         end

       end
        12 : begin
          multi_rd_en<=0;
          multi_wr_en<=0;
          response_CMD[127:96] <= CardStatus ;
          CardStatus[12:9] <= `TRAN ;
        end
   //-------------------------------------------------------------------------------//
        33 : response_CMD[127:96] <= 48;
        55 :
        begin
          response_CMD[127:96] <= CardStatus ;
          CardStatus[5] <=1;      //Next CMD is AP specific CMD
          appendCrc<=1;
        end
        41 :
        begin
         if (cardIdentificationState) begin
            if (lastCMD != 55 && outDelayCnt==0) begin
               $fdisplay(sdModel_file_desc, "**Error in sequnce, CMD 55 should precede 41 in Startup state") ;
               $display( "**Error in sequnce, CMD 55 should precede 41 in Startup state") ;
               CardStatus[3]<=1;
            end
            else begin
             responseType=3;
             response_CMD[127:96] <= OCR;
             appendCrc<=0;
             CardStatus[5] <=0;
            if (Busy==1)
              CardStatus[12:9] <=1;
           end
        end
       end

    endcase
     ValidCmd<=1;
     crcIn<=0;

     outDelayCnt<=outDelayCnt+1;
     if (outDelayCnt==`outDelay)
       crcRst<=1;
     oeCmd<=1;
     cmdOut<=1;
     response_CMD[135:134] <=0;

    if (responseType != 3)
       if (!add_wrong_cmd_indx)
         response_CMD[133:128] <=inCmd[45:40];
      else
         response_CMD[133:128] <=0;

    if (responseType == 3)
       response_CMD[133:128] <=6'b111111;

     lastCMD <=inCmd[45:40];
    end
   end



 endcase
end

always @ ( negedge sdClk) begin
 case(state)

SEND_CMD: begin
     crcRst<=0;
    if (response_S == 48)
     crcEn<=1;
    else if (response_S == 136 && cmdWrite == 6)
     crcEn<=1;
     cmdWrite<=cmdWrite+1;
    if (response_S!=0)
     cmdOut<=0;
   else
      cmdOut<=1;

    if ((cmdWrite>0) &&  (cmdWrite < response_S-8)) begin
      cmdOut<=response_CMD[135-cmdWrite];
      crcIn<=response_CMD[134-cmdWrite];
      if (cmdWrite >= response_S-9)
       crcEn<=0;
    end
   else if (cmdWrite!=0) begin
     crcEn<=0;
     if (add_wrong_cmd_crc) begin
        cmdOut<=0;
        crcCnt<=crcCnt+1;
     end
     else begin
     cmdOut<=crcOut[6-crcCnt];
     crcCnt<=crcCnt+1;
     //if (responseType == 3)
     //      cmdOut<=1;
    end
   end
  if (cmdWrite >= response_S-1) begin
    cmdOut<=1;
    $display("response send done");
    end
  end
 endcase
end



integer outdly_cnt;

always @ (posedge sdClk) begin

  case (dataState)
  DATA_IDLE: begin
      InbuffStatus<=0;
      oeDat<=0;
      datOut<=8'hff;
//      set_block <= 0;
  end

  READ_WAITS: begin
      oeDat<=0;
      crcDat_rst<=0;
      crcDat_en<=1;
      crcDat_in<=0;
      crc_c<=15;//
      crc_ok<=1;
  end
  READ_DATA: begin


    InbuffStatus<=1;
    if (transf_cnt<`BLOCKSIZE) begin
      if(BusWidth == 4) begin
       if (wptr)
         Inbuff[block_cnt][3:0] <= dat[3:0];
       else
         Inbuff[block_cnt][7:4] <= dat[3:0];
       end
      if(BusWidth == 1) begin
       if (wptr_1 == 0)
         Inbuff[block_cnt][7] <= dat[0];
       else if (wptr_1 == 1)
         Inbuff[block_cnt][6] <= dat[0];
       else if (wptr_1 == 2)
         Inbuff[block_cnt][5] <= dat[0];
       else if (wptr_1 == 3)
         Inbuff[block_cnt][4] <= dat[0];
       else if (wptr_1 == 4)
         Inbuff[block_cnt][3] <= dat[0];
       else if (wptr_1 == 5)
         Inbuff[block_cnt][2] <= dat[0];
       else if (wptr_1 == 6)
         Inbuff[block_cnt][1] <= dat[0];
       else if (wptr_1 == 7)
         Inbuff[block_cnt][0] <= dat[0];
       end
      if(BusWidth == 8) begin
      	 Inbuff[block_cnt][7:0] <= dat;
       end
       if (!add_wrong_data_crc)
          crcDat_in<=dat;
        else
          crcDat_in<=8'b10101010;

       crc_ok<=1;
      if(BusWidth == 4) begin
       if (wptr)
        begin
         block_cnt<=block_cnt+1;
         transf_cnt<=transf_cnt+1;   // 2013-12-23
        end
       wptr<=~wptr;
      end
      if(BusWidth == 1) begin
       if (wptr_1 == 7)
        begin
         block_cnt<=block_cnt+1;
         transf_cnt<=transf_cnt+1;   // 2013-12-23
        end
       wptr_1<=wptr_1 + 1;
      end
      if(BusWidth == 8) begin
      	 block_cnt<=block_cnt+1;
         transf_cnt<=transf_cnt+1;   // 2013-12-23
       end

    end
    else if  ( transf_cnt <= (`BLOCKSIZE +`BIT_CRC_CYCLE-1)) begin
       transf_cnt<=transf_cnt+1;
       crcDat_en<=0;
       last_din <=dat;

       if (transf_cnt> `BLOCKSIZE ) begin
        crc_c<=crc_c-1;
         if(BusWidth == 4) begin
          if  (crcDat_out[0][crc_c] != last_din[0]) begin
           crc_ok<=0;
            $display("4 bit check crc not right of dat0");
           end
          if  (crcDat_out[1][crc_c] != last_din[1]) begin
           crc_ok<=0;
            $display("4 bit check crc not right of dat1");
            end
          if  (crcDat_out[2][crc_c] != last_din[2]) begin
           crc_ok<=0;
            $display("4 bit check crc not right of dat2");
           end
          if  (crcDat_out[3][crc_c] != last_din[3]) begin
           crc_ok<=0;
            $display("4 bit check crc not right of dat3");
           end
         end
         else if (BusWidth == 1) begin
          if  (crcDat_out[0][crc_c] != last_din[0]) begin
           crc_ok<=0;
            $display("1 bit check crc not right of dat0");
           end
          end
         else if(BusWidth == 8) begin
          if  (crcDat_out[0][crc_c] != last_din[0]) begin
           crc_ok<=0;
           $display("8 bit check crc not right of dat0");
           end
          if  (crcDat_out[1][crc_c] != last_din[1]) begin
           crc_ok<=0;
           $display("8 bit check crc not right of dat1");
           end
          if  (crcDat_out[2][crc_c] != last_din[2]) begin
           crc_ok<=0;
           $display("8 bit check crc not right of dat2");
           end
          if  (crcDat_out[3][crc_c] != last_din[3]) begin
           crc_ok<=0;
           $display("8 bit check crc not right of dat3");
           end
          if  (crcDat_out[4][crc_c] != last_din[4]) begin
           crc_ok<=0;
           $display("8 bit check crc not right of dat4");
           end
          if  (crcDat_out[5][crc_c] != last_din[5]) begin
           crc_ok<=0;
           $display("8 bit check crc not right of dat5");
           end
          if  (crcDat_out[6][crc_c] != last_din[6]) begin
           crc_ok<=0;
           $display("8 bit check crc not right of dat6");
           end
          if  (crcDat_out[7][crc_c] != last_din[7]) begin
           crc_ok<=0;
           $display("8 bit check crc not right of dat7");
           end
         end
      end
    end
  end

  WRITE_FLASH: begin
     oeDat<=1;
     block_cnt <=0;
     wptr<=0;
     transf_cnt<=0;
     crcDat_rst<=1;
     crcDat_en<=0;
     crcDat_in<=0;


  end

  endcase


end


reg stop_r;
always @ (posedge sdClk) begin
if(stop == 1'b1)
  stop_r <= 1'b1;
else if(dataState == DATA_IDLE)
  stop_r <= 1'b0;
end

reg data_send_index;
reg [2:0] data_send_index_1bit;
integer write_out_index;
always @ (posedge sdClk) begin

  case (dataState)
  DATA_IDLE: begin
     write_out_index<=0;
     transf_cnt<=0;
     data_send_index<=0;
     data_send_index_1bit <= 3'd0;
     outdly_cnt<=0;
     flash_write_cnt<=0;
     crcDat_in<=0;
     Block_wr_en<= 0;
  end


   WRITE_DATA: begin
      oeDat<=1;
      outdly_cnt<=outdly_cnt+1;

           Block_rd_en<=0;
//--------------------------2010-08-09------------------------//
      if( Block_rd_cnt==0 ) begin
        if(boot == 1)
         BlockAddr = 32'h0;
        else
         BlockAddr = inCmd[39:8];
        end
      else
         BlockAddr = Adrr_RD_REG;
//------------------------------------------------------------//

      if ( outdly_cnt == `DLY_TO_OUTP)
       begin
        if(block_num != 0) begin
       	block_num <= block_num - 1;
       	end
      	transf_cnt <= 11'b1;
       end
      else if ( outdly_cnt > `DLY_TO_OUTP) begin
         crcDat_en<=1;
         crcDat_rst<=0;
      end
      else  begin
        crcDat_en<=0;
        crcDat_rst<=1;
        oeDat<=1;
        crc_c<=16;
     end

       if (transf_cnt==1) begin
          last_din <= 0;
          crcDat_in <= 0;
          transf_cnt <= 11'h2;
        end

        else if ( (transf_cnt>=2) && (transf_cnt<=`BLOCKSIZE + 1'b1)) begin
        if (BusWidth == 4)  begin
          data_send_index<=~data_send_index;
          if (!data_send_index) begin
             last_din[3:0]<=FLASHmem[BlockAddr+(write_out_index)][7:4];
             crcDat_in[3:0]<= FLASHmem[BlockAddr+(write_out_index)][7:4];
          end
          else begin
          	 transf_cnt <= transf_cnt+1;
          	 write_out_index<=write_out_index+1;
             last_din[3:0]<=FLASHmem[BlockAddr+(write_out_index)][3:0];
             if (!add_wrong_data_crc)
               crcDat_in[3:0]<= FLASHmem[BlockAddr+(write_out_index)][3:0];
             else
               crcDat_in[3:0]<=4'b1010;
          end
        end

        else if (BusWidth == 1)  begin
          data_send_index_1bit<=data_send_index_1bit + 1;
          if (data_send_index_1bit == 0)
          begin
             last_din[0]<=FLASHmem[BlockAddr+(write_out_index)][7];
             crcDat_in[0]<= FLASHmem[BlockAddr+(write_out_index)][7];
          end
          else if (data_send_index_1bit == 1) begin
             last_din[0]<=FLASHmem[BlockAddr+(write_out_index)][6];
             crcDat_in[0]<= FLASHmem[BlockAddr+(write_out_index)][6];
          end
          else if (data_send_index_1bit == 2) begin
             last_din[0]<=FLASHmem[BlockAddr+(write_out_index)][5];
             crcDat_in[0]<= FLASHmem[BlockAddr+(write_out_index)][5];
          end
          else if (data_send_index_1bit == 3) begin
             last_din[0]<=FLASHmem[BlockAddr+(write_out_index)][4];
             crcDat_in[0]<= FLASHmem[BlockAddr+(write_out_index)][4];
          end
          else if (data_send_index_1bit == 4) begin
             last_din[0]<=FLASHmem[BlockAddr+(write_out_index)][3];
             crcDat_in[0]<= FLASHmem[BlockAddr+(write_out_index)][3];
          end
          else if (data_send_index_1bit == 5) begin
             last_din[0]<=FLASHmem[BlockAddr+(write_out_index)][2];
             crcDat_in[0]<= FLASHmem[BlockAddr+(write_out_index)][2];
          end
          else if (data_send_index_1bit == 6) begin
             last_din[0]<=FLASHmem[BlockAddr+(write_out_index)][1];
             crcDat_in[0]<= FLASHmem[BlockAddr+(write_out_index)][1];
          end
          else if (data_send_index_1bit == 7) begin
             last_din[0]<=FLASHmem[BlockAddr+(write_out_index)][0];
             crcDat_in[0]<= FLASHmem[BlockAddr+(write_out_index)][0];
             transf_cnt <= transf_cnt+1;
             write_out_index<=write_out_index+1;
          end
        end

        else if (BusWidth == 8)  begin
             last_din<=FLASHmem[BlockAddr+(write_out_index)][7:0];
             transf_cnt <= transf_cnt+1;
             write_out_index<=write_out_index+1;
             if (!add_wrong_data_crc)
             crcDat_in<= FLASHmem[BlockAddr+(write_out_index)][7:0];
             else
             crcDat_in<=8'b10101010;
             end

          datOut<= last_din;
       end


         else if ( transf_cnt == `BLOCKSIZE + 2'd2  ) begin
       	     transf_cnt <= transf_cnt+1;
             crcDat_en<=0;
             datOut<= last_din;
         end

       else if (transf_cnt>`BLOCKSIZE + 2'd2 & crc_c!=0) begin
       	 transf_cnt <= transf_cnt+1;
         crcDat_en<=0;
         crc_c<=crc_c-1;
         if (crc_c<= 16) begin
          if (BusWidth == 4)  begin
         datOut[0]<=crcDat_out[0][crc_c-1];
         datOut[1]<=crcDat_out[1][crc_c-1];
         datOut[2]<=crcDat_out[2][crc_c-1];
         datOut[3]<=crcDat_out[3][crc_c-1];
       end
         else if (BusWidth == 1)
         begin
         datOut[0]<=crcDat_out[0][crc_c-1];
         end
          else if (BusWidth == 8)   begin
         datOut[0]<=crcDat_out[0][crc_c-1];
         datOut[1]<=crcDat_out[1][crc_c-1];
         datOut[2]<=crcDat_out[2][crc_c-1];
         datOut[3]<=crcDat_out[3][crc_c-1];
         datOut[4]<=crcDat_out[4][crc_c-1];
         datOut[5]<=crcDat_out[5][crc_c-1];
         datOut[6]<=crcDat_out[6][crc_c-1];
         datOut[7]<=crcDat_out[7][crc_c-1];
       end

       end
       end
       else if (transf_cnt==`BLOCKSIZE + 11'd19) begin
       	  crcDat_en<=0;
       	  transf_cnt <= transf_cnt+1;
          datOut<=8'hff;
        if( multi_rd_en ) begin
         if(set_block == 0) begin
         Adrr_RD_REG <= write_out_index+Adrr_RD_REG;
         oeDat<=0;
         CardStatus[12:9] <= `DATAS;
        end
         else if (set_block == 1 && block_num == 0) begin
         	CardStatus[12:9] <= `TRAN;
         end
         else begin
         Adrr_RD_REG <= write_out_index+Adrr_RD_REG;
         oeDat<=0;
         CardStatus[12:9] <= `DATAS;
        end
        end
        else  begin
         CardStatus[12:9] <= `TRAN;
        end
         Block_rd_en <= 1;
      end

  end



  WRITE_FLASH: begin
    flash_write_cnt<=flash_write_cnt+1;
     CardStatus[12:9] <= `PRG;
      datOut[0]<=0;
       datOut[1]<=1;
       datOut[2]<=1;
       datOut[3]<=1;
    if (flash_write_cnt == 0 && block_num != 0 ) begin
      block_num <= block_num - 1;
      datOut<=1;
    end
    else if(flash_write_cnt == 1)
     datOut[0]<=1;
    else if(flash_write_cnt == 2)
     datOut[0]<=0;

    //send crc state: 010
    else if ((flash_write_cnt > 2) && (flash_write_cnt < 7)) begin
      if (crc_ok)
        datOut[0] <=okcrctoken[6-flash_write_cnt];
      else
        datOut[0] <= invalidcrctoken[6-flash_write_cnt];
    end
    else if  ((flash_write_cnt >= 7) && (flash_write_cnt <= 262)) begin
       datOut[0]<=0;
       flash_blockwrite_cnt<=flash_blockwrite_cnt+2;
       FLASHmem[BlockAddr+(flash_blockwrite_cnt)]<=Inbuff[flash_blockwrite_cnt];
       FLASHmem[BlockAddr+(flash_blockwrite_cnt+1)]<=Inbuff[flash_blockwrite_cnt+1];

    end
    else begin
      datOut<=1;
      InbuffStatus<=0;
      if ( multi_wr_en ) begin
      	 if(stop_r == 1'b1)
         CardStatus[12:9] <= `TRAN;
         else if(set_block == 0) begin
         Block_wr_en<= 1;
         BlockAddr <= Adrr_WR_REG;
         CardStatus[12:9] <= `RCV;
         end
         else if(block_num == 0 && set_block == 1)  begin
//         set_block <= 0;
         CardStatus[12:9] <= `TRAN;
         end
      	 else begin
         Block_wr_en<= 1;
         BlockAddr <= Adrr_WR_REG;
         CardStatus[12:9] <= `RCV;
//         block_num <= block_num - 1;
         end
      end
      else
         CardStatus[12:9] <= `TRAN;
    end
  end
endcase
end

integer sdModel_file_desc;
/*
initial
begin
  sdModel_file_desc = $fopen("../log/sd_model.log");
  if (sdModel_file_desc < 2)
  begin
    $display("*E Could not open/create testbench log file in /log/ directory!");
    $finish;
  end
end
*/
//--------------------------2010-08-09---------------------------//
reg   Block_wr_en_r1;
reg   Block_wr_en_r2;

reg   wr_en;

always @ (posedge sdClk )
begin
   Block_wr_en_r1<=Block_wr_en;
   Block_wr_en_r2<=Block_wr_en_r1;
end

reg Block_en;
always @ (posedge sdClk )
begin
 if( Block_wr_en_r1 && ~Block_wr_en_r2 ) begin
   wr_en<=1;
 end else
   wr_en<=0;
end

always @ (posedge wr_en )
begin
   if ( Block_wr_cnt == BLOCK_CNT_REG-1 )
      Block_wr_cnt<=0;
   else begin
      Block_wr_cnt<=Block_wr_cnt+1'b1;
      Adrr_WR_REG <= 512+Adrr_WR_REG;
     end
end

always @ (posedge sdClk )
begin
 if( block_num == 0 && set_block <=1 && dataState == DATA_IDLE)
  set_block <=0;
end

always @ (posedge sdClk )
begin
 if( Block_wr_cnt==BLOCK_CNT_REG-1 )
  multi_wr_en <=0;
end

reg   Block_rd_en_r1;
reg   Block_rd_en_r2;

reg   rd_en;

always @ (posedge sdClk )
begin
   Block_rd_en_r1<=Block_rd_en;
   Block_rd_en_r2<=Block_rd_en_r1;
end

reg Block_en_rd;
always @ (posedge sdClk )
begin
 if( Block_rd_en_r1 && ~Block_rd_en_r2 ) begin
   rd_en<=1;
 end else
   rd_en<=0;
end

always @ (posedge rd_en )
begin
   if ( Block_rd_cnt == BLOCK_CNT_REG-1 )
      Block_rd_cnt<=0;
   else
      Block_rd_cnt<=Block_rd_cnt+1'b1;
end

always @ (posedge sdClk )
begin
 if( Block_rd_cnt==BLOCK_CNT_REG-1 )
  multi_rd_en <=0;
end
//------------------------------------------------------------------//


integer mem_i;
reg reset;

task ResetCard; //  MAC registers
begin
  reset <= 1'b1;
  add_wrong_data_crc<=0;
  add_wrong_cmd_indx<=0;
  add_wrong_cmd_crc<=0;
  cardIdentificationState<=1;
  state<=IDLE;
  dataState<=DATA_IDLE;
  Busy<=1;
  oeCmd<=0;
  crcCnt<=0;
  CardTransferActive<=0;
  qCmd<=1;
  oeDat<=0;
  cmdOut<=0;
  cmdWrite<=0;
  block_num <= 8'h00;
  multi_wr_en <= 1'b0;
  set_block <= 1'b0;
  stop_r <= 1'b0;
 //---------------------------------//
  rd_en<=0;
  wr_en<=0;
  Block_wr_en_r1<=0;
  Block_wr_en_r2<=0;
  Block_wr_en<= 0;
  Block_rd_en_r1<=0;
  Block_rd_en_r2<=0;
  Block_rd_en<=0;

  InbuffStatus<=0;
  datOut<=0;
  inCmd<=0;
  BusWidth<=1;
  responseType=0;
  crcIn<=0;
  response_S<=0;
  set_block <= 0;
  crcEn<=0;
  crcRst<=0;
  cmdRead<=0;
  ValidCmd<=0;
  inValidCmd=0;
  appendCrc<=0;
  RCA<= `RCASTART;
  OCR<= `OCRSTART;
  CardStatus <= `STATUSSTART;
  CID<=`CIDSTART;
  response_CMD<=0;
  outDelayCnt<=0;
  crcDat_rst<=1;
  crcDat_en<=0;
  crcDat_in<=0;
  transf_cnt<=0;
  BlockAddr<=0;
  block_cnt <=0;
  stop <= 0;
  wptr<=0;
  wptr_1<=0;
  transf_cnt<=0;
  crcDat_rst<=1;
  crcDat_en<=0;
  crcDat_in<=0;
flash_write_cnt<=0;
flash_blockwrite_cnt<=0;
Adrr_RD_REG<=0;
Adrr_WR_REG<=0;
Block_wr_cnt<=0;
Block_rd_cnt<=0;
multi_rd_en<=0;
multi_wr_en<=0;

//mem intial
for(mem_i=0;mem_i<=10240;mem_i=mem_i+1)
begin
FLASHmem[mem_i] <= 8'hff;
end

end
endtask


endmodule
